package dao

import (
	"encoding/json"
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
	"gorm.io/gorm"
	"strconv"
	"time"
	"web_api_gin/core"
	"web_api_gin/dto"
	"web_api_gin/entity"
	"web_api_gin/util"
)

var SysPermission = SysPermissionDao{}

type SysPermissionDao struct {
}

// 获取所有权限列表
func (d *SysPermissionDao) GetAll(ctx *gin.Context) ([]*entity.SysPermission, error) {
	var list []*entity.SysPermission
	result := DB(ctx).Find(&list)
	return list, result.Error
}

//通过权限名称获取权限数据
func (d *SysPermissionDao) GetPermByName(ctx *gin.Context, name string) (*entity.SysPermission, error) {
	var data *entity.SysPermission
	result := DB(ctx).Where("name = ? ", name).Limit(1).Find(&data)
	return data, result.Error
}

// 通过权限ID获取权限数据
func (d *SysPermissionDao) GetPermById(ctx *gin.Context, permId uint32) (*entity.SysPermission, error) {
	var data *entity.SysPermission
	result := DB(ctx).Where("perm_id = ? ", permId).Limit(1).Find(&data)
	return data, result.Error
}

//获取权限列表
func (d SysPermissionDao) GetPermList(ctx *gin.Context, searchParams *dto.SysPermissionGetListReq) ([]*entity.SysPermission, error) {
	var list []*entity.SysPermission
	tx := DB(ctx).Model(&entity.SysPermission{})
	if searchParams.Name != "" {
		tx = tx.Where("name like ?", "%"+searchParams.Name+"%")
	}
	if searchParams.DisplayName != "" {
		tx = tx.Where("display_name like ?", "%"+searchParams.DisplayName+"%")
	}
	rs := tx.Order("order_num ASC,perm_id ASC").Find(&list)
	return list, rs.Error
}

// 通过角色ID列表获取权限ID列表
func (d *SysPermissionDao) GetPermissionIdsByRoleIds(ctx *gin.Context, roleIds []uint32) ([]uint32, error) {
	var (
		permIds  []uint32
		permList []entity.SysPermission
	)
	if len(roleIds) > 0 {
		result := DB(ctx).Where("role_id in ?", roleIds).Find(&permList)
		if result.Error != nil {
			return nil, result.Error
		}
		for _, permission := range permList {
			permIds = append(permIds, permission.PermId)
		}
	}
	return permIds, nil
}

// 获取权限列表
func (d *SysPermissionDao) GetUserPermission(ctx *gin.Context, userRoleNameList []string, userRoleIdList []uint32) ([]string, error) {
	var userPermNameList []string //用户权限名称列表
	//所有权限列表
	allPermissionList, err := d.GetAll(ctx)
	if err != nil {
		return nil, err
	}
	//权限名称列表
	if util.ArrayUtil.IsContain(userRoleNameList, "super-admin") { //超级管理员
		for _, permissions := range allPermissionList {
			userPermNameList = append(userPermNameList, permissions.Name)
		}
	} else { //非超管
		userPermIds, err := d.GetPermissionIdsByRoleIds(ctx, userRoleIdList)
		if err != nil {
			return nil, err
		}
		//数组类型转换
		var userPermIdsStr []string
		for _, v := range userPermIds {
			userPermIdsStr = append(userPermIdsStr, string(v))
		}
		for _, permissions := range allPermissionList {
			if permissions.IsPublic == true || util.ArrayUtil.IsContain(userPermIdsStr, string(permissions.PermId)) {
				userPermNameList = append(userPermNameList, permissions.Name)
			}
		}
	}
	return userPermNameList, nil
}

// 获取面包学列表数据
func (d *SysPermissionDao) GetBreadcrumbList(ctx *gin.Context, permId uint32, isRefresh bool) ([]*entity.SysPermission, error) {
	//角色名称缓存KEY
	cacheKey := "sys_perm_breadcrumb_list:" + strconv.FormatUint(uint64(permId), 10)
	cacheStr, err := core.Redis.Get(ctx, cacheKey).Result()
	var permList []*entity.SysPermission
	if err == redis.Nil || isRefresh == true {
		for permId != 0 {
			if permData, err := d.GetPermById(ctx, permId); err == nil && permData.PermId != 0 {
				permList = append([]*entity.SysPermission{permData}, permList...)
				permId = permData.ParentId
			} else {
				break
			}
		}
		//缓存角色列表
		permJsonStr, _ := json.Marshal(permList)
		if err := core.Redis.Set(ctx, cacheKey, permJsonStr, 2*time.Hour).Err(); err != nil {
			return nil, err
		}
		return permList, nil
	}
	if err := json.Unmarshal([]byte(cacheStr), &permList); err != nil {
		return nil, err
	}
	return permList, nil
}

// 保存权限数据
func (d *SysPermissionDao) SavePerm(ctx *gin.Context, data *entity.SysPermission) (int64, error) {
	var rs *gorm.DB
	if data.PermId != 0 {
		rs = DB(ctx).Where("perm_id = ?", data.PermId).Updates(data)
	} else {
		rs = DB(ctx).Create(data)
	}
	//刷新面包屑数据
	d.GetBreadcrumbList(ctx, data.PermId, true)
	return rs.RowsAffected, rs.Error
}

// 获取指定父级ID的下级菜单列表
func (d *SysPermissionDao) GetPermChildList(ctx *gin.Context, ParentId uint32) ([]*entity.SysPermission, error) {
	list := ([]*entity.SysPermission)(nil)
	rs := DB(ctx).Model(&entity.SysPermission{}).Where("parent_id = ? ", ParentId).Order("order_num ASC,perm_id ASC").Find(&list)
	return list, rs.Error
}

// 删除菜单
func (d *SysPermissionDao) DelPerm(ctx *gin.Context, permIds []uint32) (int64, error) {
	rs := DB(ctx).Delete(&entity.SysPermission{}, permIds)
	return rs.RowsAffected, rs.Error
}

// 设置排序
func (d *SysPermissionDao) SetOrder(ctx *gin.Context, form *dto.SysPermissionSetOrderRequest) error {
	var err error
	switch form.DropType {
	case "before", "after": //拖拽到某个节点之前||拖拽到某个节点之后
		//使用事务闭包
		err = DB(ctx).Transaction(func(tx *gorm.DB) error {
			var newOrderNum uint32
			if form.DropType == "before" { //插到前面取被插队的排序数字作为排序，，后续累加一保持顺序
				newOrderNum = form.DropNode.OrderNum
			} else if form.DropType == "after" { //插到后面取插队排序数字加一作为排序，后续累加一保持顺序
				newOrderNum = form.DropNode.OrderNum + 1
			}
			//事先查出菜单列表
			sameLevelPermList, err := d.GetPermChildList(ctx, form.DropNode.ParentId)
			if err != nil {
				return err
			}
			//拖拽节点更改排序数字及父级分类
			tx.Model(&entity.SysPermission{}).Where("perm_id = ? ", form.DraggingNode.PermId).Updates(entity.SysPermission{ParentId: form.DropNode.ParentId, OrderNum: newOrderNum})
			//调整之后节点的排序数字,之后的节点各加2，为插入节点留空隙
			isStartSet := false
			for _, perm := range sameLevelPermList {
				if perm.PermId == form.DropNode.PermId { //从放置节点开始后续排序自动加一
					isStartSet = true
					if form.DropType == "after" { //插入到后面，则被放置节点不处理
						continue
					}
					if form.DropType == "before" { //插入到前面，从被放置节点开始排序开始调整

					}
				}
				if isStartSet && perm.PermId != form.DraggingNode.PermId {
					newOrderNum++
					tx.Model(&entity.SysPermission{}).Where("perm_id = ? ", perm.PermId).Update("order_num", newOrderNum)
				}
			}
			//刷新面包屑数据
			d.GetBreadcrumbList(ctx, form.DraggingNode.PermId, true)
			return nil
		})
	case "inner": //拖进节点，直接更改父级ID
		err = DB(ctx).Model(&entity.SysPermission{}).Where("perm_id = ? ", form.DraggingNode.PermId).Update("parent_id", form.DropNode.PermId).Error
		//刷新面包屑数据
		d.GetBreadcrumbList(ctx, form.DraggingNode.PermId, true)
	default:
		err = errors.New("无效的节点类型")
	}
	return err
}
